D:\a\tools.proto\tools.proto\compiler\src\compiler\union.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::message::{Referenced, SizeInfo}; |
30 | | use crate::compiler::structure::{Field, FieldType, FieldView, FixedField, Structure}; |
31 | | use crate::compiler::util::store::name_index; |
32 | | use crate::compiler::{Error, Protocol}; |
33 | | use crate::model::protocol::Description; |
34 | | use std::rc::Rc; |
35 | | |
36 | | #[derive(Clone, Debug)] |
37 | | pub struct UnionField { |
38 | | pub name: String, |
39 | | pub description: Option<Description>, |
40 | | pub case: usize, |
41 | | pub item_type: Option<Referenced>, |
42 | | } |
43 | | |
44 | | impl UnionField { |
45 | 36 | pub fn from_model( |
46 | 36 | proto: &Protocol, |
47 | 36 | discriminant: &FixedField, |
48 | 36 | value: crate::model::union::UnionField, |
49 | 36 | ) -> Result<Self, Error> { |
50 | 36 | let case: usize = match &discriminant.view { |
51 | 0 | FieldView::Float { .. } => return Err(Error::FloatInUnionDiscriminant), |
52 | 28 | FieldView::Enum(r) => { |
53 | 28 | r.variants_map.get(&value.case).copied().ok_or(Error::InvalidUnionCase(value.case))?0 as usize |
54 | | } |
55 | | //TODO: Check how unions work with the new bits-type/raw-type/view-type separation |
56 | 8 | FieldView::None => value.case.parse().map_err(|_| Error::InvalidUnionCase(value.case)0 )?0 , |
57 | | }; |
58 | 36 | let item_type = value |
59 | 36 | .item_type |
60 | 36 | .map(|v| Referenced::lookup(proto, &v).ok_or(Error::UndefinedReference(v))26 ) |
61 | 36 | .transpose()?0 ; |
62 | 36 | Ok(UnionField { |
63 | 36 | name: value.name, |
64 | 36 | description: value.description, |
65 | 36 | case, |
66 | 36 | item_type, |
67 | 36 | }) |
68 | 36 | } |
69 | | } |
70 | | |
71 | | #[derive(Clone, Debug)] |
72 | | pub struct DiscriminantField { |
73 | | pub root: Rc<Structure>, |
74 | | pub leaf: Rc<Structure>, |
75 | | pub leaf_index: usize, |
76 | | pub index_list: Vec<usize>, |
77 | | } |
78 | | |
79 | | struct DiscriminantFieldIterator<'a> { |
80 | | cur: &'a Structure, |
81 | | index: std::slice::Iter<'a, usize>, |
82 | | } |
83 | | |
84 | | impl<'a> Iterator for DiscriminantFieldIterator<'a> { |
85 | | type Item = (&'a Field, bool); |
86 | | |
87 | 28 | fn next(&mut self) -> Option<Self::Item> { |
88 | 28 | let index16 = self.index.next()?12 ; |
89 | 16 | let field = &self.cur.fields[*index]; |
90 | 16 | let is_leaf = match &field.ty { |
91 | 12 | FieldType::Fixed(_) => true, |
92 | 0 | FieldType::Array(_) => std::unreachable!(), |
93 | 4 | FieldType::Struct(v) => { |
94 | 4 | self.cur = v; |
95 | 4 | false |
96 | | } |
97 | | }; |
98 | 16 | Some((field, is_leaf)) |
99 | 28 | } |
100 | | } |
101 | | |
102 | | impl DiscriminantField { |
103 | 12 | pub fn iter(&self) -> impl Iterator<Item = (&Field, bool)> { |
104 | 12 | DiscriminantFieldIterator { |
105 | 12 | cur: &self.root, |
106 | 12 | index: self.index_list.iter(), |
107 | 12 | } |
108 | 12 | } |
109 | | |
110 | 0 | pub fn get_leaf(&self) -> &Field { |
111 | 0 | &self.leaf.fields[self.leaf_index] |
112 | 0 | } |
113 | | |
114 | 42 | pub fn get_leaf_fixed(&self) -> &FixedField { |
115 | 42 | self.leaf.fields[self.leaf_index].ty.as_fixed().unwrap() |
116 | 42 | } |
117 | | |
118 | 10 | pub fn from_model(proto: &Protocol, discriminant: String) -> Result<Self, Error> { |
119 | 10 | let mut parts = discriminant.split("."); |
120 | 10 | let name = parts.next().ok_or(Error::InvalidUnionDiscriminant)?0 ; |
121 | 10 | let mut leaf = proto.structs.get(name).ok_or_else(|| Error::UndefinedReference(name.into())0 )?0 ; |
122 | 10 | let root = leaf; |
123 | 10 | let mut index_list = Vec::new(); |
124 | 12 | for sub in parts { |
125 | 12 | let (index, field) = leaf |
126 | 12 | .fields |
127 | 12 | .iter() |
128 | 12 | .enumerate() |
129 | 12 | .find(|(_, v)| v.name == sub) |
130 | 12 | .ok_or_else(|| Error::UndefinedReference(format!("{}.{}", name, sub))0 )?0 ; |
131 | 12 | index_list.push(index); |
132 | 12 | match &field.ty { |
133 | 10 | FieldType::Fixed(_) => break, |
134 | 2 | FieldType::Struct(v) => leaf = v, |
135 | 0 | FieldType::Array(_) => return Err(Error::InvalidUnionDiscriminant), |
136 | | } |
137 | | } |
138 | 10 | Ok(DiscriminantField { |
139 | 10 | root: root.clone(), |
140 | 10 | leaf: leaf.clone(), |
141 | 10 | leaf_index: index_list.last().copied().unwrap(), |
142 | 10 | index_list, |
143 | 10 | }) |
144 | 10 | } |
145 | | } |
146 | | |
147 | | #[derive(Clone, Debug)] |
148 | | pub struct Union { |
149 | | pub name: String, |
150 | | pub discriminant: DiscriminantField, |
151 | | pub description: Option<Description>, |
152 | | pub cases: Vec<UnionField>, |
153 | | pub size: SizeInfo, |
154 | | } |
155 | | |
156 | | impl Union { |
157 | 13 | pub fn has_content(&self) -> bool { |
158 | 22 | self.cases.iter().any(|v| v.item_type.is_some()) |
159 | 13 | } |
160 | | |
161 | 10 | pub fn from_model(proto: &Protocol, value: crate::model::union::Union) -> Result<Self, Error> { |
162 | 10 | let discriminant = DiscriminantField::from_model(proto, value.discriminant)?0 ; |
163 | 10 | let cases = value |
164 | 10 | .cases |
165 | 10 | .into_iter() |
166 | 36 | .map(|v| UnionField::from_model(proto, discriminant.get_leaf_fixed(), v)) |
167 | 10 | .collect::<Result<Vec<UnionField>, Error>>()?0 ; |
168 | 36 | let is_element_dyn_sized = cases.iter().any(10 |v| { |
169 | 36 | v.item_type |
170 | 36 | .as_ref() |
171 | 36 | .map(|v| match v26 { |
172 | 22 | Referenced::Struct(_) => false, |
173 | 4 | Referenced::Message(v) => v.size.is_element_dyn_sized, |
174 | 36 | }26 ) |
175 | 36 | .unwrap_or_default() |
176 | 36 | }); |
177 | 16 | let is_dyn_sized = cases.iter().any(|v| { |
178 | 16 | v.item_type |
179 | 16 | .as_ref() |
180 | 16 | .map(|v| match v6 { |
181 | 2 | Referenced::Struct(_) => false, |
182 | 4 | Referenced::Message(v) => v.size.is_dyn_sized, |
183 | 16 | }6 ) |
184 | 16 | .unwrap_or_default() |
185 | 16 | }); |
186 | 10 | Ok(Union { |
187 | 10 | name: value.name, |
188 | 10 | discriminant, |
189 | 10 | description: value.description, |
190 | 10 | cases, |
191 | 10 | size: SizeInfo { |
192 | 10 | is_element_dyn_sized, |
193 | 10 | is_dyn_sized, |
194 | 10 | }, |
195 | 10 | }) |
196 | 10 | } |
197 | | } |
198 | | |
199 | | name_index!(Union => name); |